// synch.cc 
//	Routines for synchronizing threads.  Three kinds of
//	synchronization routines are defined here: semaphores, locks 
//   	and condition variables (the implementation of the last two
//	are left to the reader).
//
// Any implementation of a synchronization routine needs some
// primitive atomic operation.  We assume Nachos is running on
// a uniprocessor, and thus atomicity can be provided by
// turning off interrupts.  While interrupts are disabled, no
// context switch can occur, and thus the current thread is guaranteed
// to hold the CPU throughout, until interrupts are reenabled.
//
// Because some of these routines might be called with interrupts
// already disabled (Semaphore::V for one), instead of turning
// on interrupts at the end of the atomic operation, we always simply
// re-set the interrupt state back to its original value (whether
// that be disabled or enabled).
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "synch.h"
#include "system.h"

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	Initialize a semaphore, so that it can be used for synchronization.
//
//	"debugName" is an arbitrary name, useful for debugging.
//	"initialValue" is the initial value of the semaphore.
//----------------------------------------------------------------------

Semaphore::Semaphore(char* debugName, int initialValue)
{
    name = debugName;
    value = initialValue;
    queue = new List;
}

//----------------------------------------------------------------------
// Semaphore::Semaphore
// 	De-allocate semaphore, when no longer needed.  Assume no one
//	is still waiting on the semaphore!
//----------------------------------------------------------------------

Semaphore::~Semaphore()
{
    delete queue;
}

//----------------------------------------------------------------------
// Semaphore::P
// 	Wait until semaphore value > 0, then decrement.  Checking the
//	value and decrementing must be done atomically, so we
//	need to disable interrupts before checking the value.
//
//	Note that Thread::Sleep assumes that interrupts are disabled
//	when it is called.
//----------------------------------------------------------------------

void
Semaphore::P()
{
  IntStatus oldLevel = interrupt->SetLevel(IntOff);	// disable interrupts
  
  while (value == 0) { 		// semaphore not available
    queue->Append((void *)currentThread);	// so go to sleep
    currentThread->Sleep();
  } 
  value--; 					// semaphore available, 
						// consume its value
  
  (void) interrupt->SetLevel(oldLevel);	// re-enable interrupts
}

//----------------------------------------------------------------------
// Semaphore::V
// 	Increment semaphore value, waking up a waiter if necessary.
//	As with P(), this operation must be atomic, so we need to disable
//	interrupts.  Scheduler::ReadyToRun() assumes that threads
//	are disabled when it is called.
//----------------------------------------------------------------------

void
Semaphore::V()
{
    Thread *thread;
    IntStatus oldLevel = interrupt->SetLevel(IntOff);

    thread = (Thread *)queue->Remove();
    if (thread != NULL)	   // make thread ready, consuming the V immediately
	scheduler->ReadyToRun(thread);
    value++;
    (void) interrupt->SetLevel(oldLevel);
}

// Dummy functions -- so we can compile our later assignments 
// Note -- without a correct implementation of Condition::Wait(), 
// the test case in the network assignment won't work!
Lock::Lock(char* debugName) {
  name = debugName;
  lockQueue = new List;
  lockOwner = NULL;
}

//Destruction
Lock::~Lock() {
  delete lockQueue;
}


//Bool function to check if current thread owns the lock
bool Lock::isHeldByCurrentThread(){
  if(currentThread == lockOwner)
    return true;
  
  return false;
  //return (currentThread == lockOwner); 
}

void Lock::Acquire() {
  //Disable the interrupt
  IntStatus old = interrupt->SetLevel(IntOff);
  //if(isHeldByCurrentThread()){
    //if "I'm the lock owner", restore the interrupt
  //  (void) interrupt->SetLevel(old);
  //}

  //else 
  if (lockOwner == NULL){
    //if "lock available", make myself lockowner
    lockOwner = currentThread;
    //cout<<"lock acquired"<<endl;
  }
  else if (!isHeldByCurrentThread()){
    //if "lock not available", add thread to a lockQueue,
    //and put myself to sleep
    lockQueue ->Append((void*)currentThread);
    currentThread -> Sleep();
  }

  //enable interrupts
  (void)interrupt->SetLevel(old);
}


void Lock::Release() {
  
  //disable interrupts
  IntStatus old = interrupt->SetLevel(IntOff);
 
  //makes sure that the current thread is the lock owner
  if (isHeldByCurrentThread()){
    
    //check if waitlist is empty
    if (lockQueue->IsEmpty()){
      //cout<<"lock released"<<endl;
      //set no one as the lock owner
      lockOwner = NULL;
    }
    
    else{
      //get first thread off the waiting list
      lockOwner = (Thread *)lockQueue->Remove();
      //cout<<"lock passed"<<endl;
      //wake up the new lock owner
      scheduler->ReadyToRun(lockOwner);
    }
  }
  
  else {
    //print error message if not the lock owner
    cout<<"This thread did not own the Lock"<<endl;
  }
  
  //Enable interrupts
  (void)interrupt->SetLevel(old);
}


//Contruction for condition
//
Condition::Condition(char* debugName) { 
  name = debugName;
  cvWaitlist = new List;
  cLock = NULL;
}

//Destruction for Condition
Condition::~Condition() { 
  delete cvWaitlist;
  cLock = NULL;
}


void Condition::Wait(Lock* conditionLock) { 
 
  //disable interrupts
  IntStatus old = interrupt->SetLevel(IntOff);

  if (conditionLock->isHeldByCurrentThread()){
    //if (cLock == NULL){
    cLock = conditionLock;
      //}
        
    //leave the monitor
    conditionLock->Release();
    
    //put on waiting list for CV
    cvWaitlist->Append((void *) currentThread);
    
    //put the thread to sleep and wait for signal
    currentThread->Sleep();
    
    //obtain the lock
    conditionLock->Acquire();
  }

  //enable interrupts
  (void)interrupt->SetLevel(old);

}


void Condition::Signal(Lock* conditionLock) { 
  //disable interrupts
  IntStatus old = interrupt->SetLevel(IntOff);
  //cout<<"condition lock: "<<conditionLock<<endl;

  if (conditionLock != cLock && cLock != NULL){
  cout<<"Thread did not have the correct Lock"<<endl;
  }else 
  if (!cvWaitlist->IsEmpty())
    {
      //wake up next thread on the wait list
      //make sure current thread has the lock
      if(conditionLock->isHeldByCurrentThread())
	{
	  //allocate data for a temporary thread
	  Thread* nextThread;
	  //get the next thread on the wait list
	  nextThread = (Thread *)cvWaitlist->Remove();
	  //wake that thread up
	  scheduler->ReadyToRun(nextThread);
	} else{
	  //error message
	  cout<<"This thread did not own the Lock"<<endl;
	}
    }
  
  //enable interrupts
  (void)interrupt->SetLevel(old);
}


void Condition::Broadcast(Lock* conditionLock) { 
  //signal all threads on the wait list
  while (!cvWaitlist->IsEmpty()){
    Signal(conditionLock);
  }
}
